﻿namespace VectorPicsEditor
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Shapes;
    using VectorPicsEditor.Controls;
    using VectorPicsEditor.Helpers;
    using Action = Actions.Action;

    public enum ShapeType {Pencil, Line, Rectangle, Ellipse, Selector, TextBox, TextBlock}

    public partial class CanvasWrapper : UserControl
    {
        private bool changed;

        private PropertiesBar propertiesBar;
        private ShapeType? currShapeType;
        private Point mousePos;
        private FrameworkElement curShape;
        private ShapeWrapper settingsShape;

        private List<ShapeWrapper> shapes;
        private NameGenerator nameGenerator;


        public List<Action> Actions { get; set; }

        public CanvasWrapper(PropertiesBar propertiesBar)
        {
            this.InitializeComponent();

            this.nameGenerator = new NameGenerator();
            this.Actions = new List<Action>();
            this.shapes = new List<ShapeWrapper>();
            this.propertiesBar = propertiesBar;
            this.propertiesBar.SelectedShapeChanged += this.PropertyShapeSelected;
            this.currShapeType = null;
            this.mousePos = new Point(-1, -1);
            this.curShape = null;
            this.changed = false;

            Rectangle tmpShape = new Rectangle();
            tmpShape.Stroke = new SolidColorBrush(Colors.Black);
            tmpShape.StrokeThickness = 1;
            Canvas.SetZIndex(tmpShape, 0);
            settingsShape = new ShapeWrapper(tmpShape, "");
            this.propertiesBar.AddShape(settingsShape);

        }

        public void ChangeBackGround(Color clr)
        {
            this.Canvas.Background = new SolidColorBrush(clr);
        }

        public void ResizeCanvasElements(double widthKoef, double heightKoef)
        {
            this.RemoveSelection();
            foreach (UIElement elem in Canvas.Children)
            {
                if (elem is Line)
                {
                    (elem as Line).X1 *= widthKoef;
                    (elem as Line).X2 *= widthKoef;

                    (elem as Line).Y1 *= heightKoef;
                    (elem as Line).Y2 *= heightKoef;
                    continue;
                }
                Canvas.SetLeft(elem, (int)(Canvas.GetLeft(elem) * widthKoef));
                Canvas.SetTop(elem, (int)(Canvas.GetTop(elem) * heightKoef));
                if (elem is Shape)
                {
                    (elem as Shape).Height = (int)((elem as Shape).Height*heightKoef);
                    (elem as Shape).Width = (int)((elem as Shape).Width*widthKoef);
                }
                if (elem is TextBlock)
                {
                    (elem as TextBlock).FontSize = (elem as TextBlock).FontSize * widthKoef;
                }
            }
            this.changed = true;
        }

        public bool Changed
        {
            get
            {
                bool shapesChanged = false;
                foreach(ShapeWrapper shw in this.shapes)
                {
                    if (shw.WasChanged)
                    {
                        shapesChanged = true;
                        break;
                    }
                }
                return shapesChanged || this.changed;
            }
            set
            {
                this.changed = value;
                if (value == false)
                {
                    foreach (ShapeWrapper shw in this.shapes)
                    {
                        shw.WasChanged = false;
                    }
                }
            }
        }

        public double CanvasWidth
        {
            get
            {
                return this.Canvas.Width;
            }
            set
            {
                this.Canvas.Width = value;
                this.changed = true;
            }
        }

        public double CanvasHeight
        {
            get
            {
                return this.Canvas.Height;
            }
            set
            {
                this.Canvas.Height = value;
                this.changed = true;
            }
        }

        public void AddShape(ShapeWrapper shape)
        {
            this.shapes.Add(shape);
            if (!(shape.Shape is Polyline))
            {
                this.propertiesBar.AddShape(shape);
                shape.MouseSelected += this.ShapeSelected;
            }
            shape.RemoveShape += this.ShapeRemove;
            this.changed = true;
        }

        private void ShapeRemove(object sender, KeyboardEventArgs e)
        {
            FrameworkElement toRemove = sender as FrameworkElement;
            this.RemoveSelection();
            this.Canvas.Children.Remove(toRemove);
        }

        private void ShapeSelected(object sender, MouseButtonEventArgs e)
        {
            if (sender is Line)
            {
                this.RemoveSelection();
            }
            if (currShapeType == ShapeType.Selector)
            {
                e.Handled = true;
                if (sender as ShapeWrapper != null)
                    this.SelectShape(sender as ShapeWrapper);
            }
        }

        private void PropertyShapeSelected(object sender, EventArgs e)
        {
            this.SelectShape(sender as ShapeWrapper);
        }

        public ShapeWrapper GetShape(string name)
        {
            return this.shapes.FirstOrDefault(s => s.Name == name);
        }

        public void SelectShape(ShapeWrapper shape)
        {
            this.RemoveSelection();
            if (shape.Shape is Line)
            {
                LineSelector selector1 = new LineSelector(shape.Shape as Line, this.Canvas,true);
                selector1.SetValue(Canvas.ZIndexProperty, Int32.MaxValue);
                this.Canvas.Children.Add(selector1);
                LineSelector selector2 = new LineSelector(shape.Shape as Line, this.Canvas, false);
                selector2.SetValue(Canvas.ZIndexProperty, Int32.MaxValue);
                this.Canvas.Children.Add(selector2);
                this.curShape = shape.Shape;
                this.propertiesBar.SelectShape(shape);
            }
            else
            {
                Selector selector = new Selector(shape.Shape, this.Canvas);
                selector.SetValue(Canvas.ZIndexProperty, Int32.MaxValue);
                this.Canvas.Children.Add(selector);
                this.curShape = shape.Shape;
                this.propertiesBar.SelectShape(shape);
            }
            this.changed = true;
        }

        public void RemoveSelection()
        {
            bool clean = false;
            while(!clean)
            {
                clean = true;
                foreach (FrameworkElement ch in this.Canvas.Children)
                {
                    clean = false;
                    if (ch is Selector || ch is LineSelector)
                    {
                        this.Canvas.Children.Remove(ch);
                        break;
                    }
                    clean = true;
                }
            }
        }

        private void showPreviewSettings()
        {
            this.propertiesBar.SelectShape(settingsShape);
        }

        public void StartDrawing(ShapeType shp)
        {
            if (this.currShapeType == ShapeType.TextBox && this.curShape is TextBox)
            {
                this.TextBoxLostFocus(this.curShape, null);
            }
            this.showPreviewSettings();
            this.RemoveSelection();
            this.currShapeType = shp;
        }

        private void TextBoxLostFocus(object sender, RoutedEventArgs e)
        {
            if (!(this.curShape is TextBox))
                return;
            this.currShapeType = ShapeType.TextBlock;

            if ((this.curShape as TextBox).Name == "New")
            {
                this.curShape = new TextBlock();
                (this.curShape as TextBlock).Text = (sender as TextBox).Text;
                (this.curShape as TextBlock).Width = (sender as TextBox).ActualWidth;
                (this.curShape as TextBlock).Height = (sender as TextBox).ActualHeight;
                (this.curShape as TextBlock).FontFamily = (sender as TextBox).FontFamily;
                (this.curShape as TextBlock).FontSize = (sender as TextBox).FontSize;
                (this.curShape as TextBlock).FontStretch = (sender as TextBox).FontStretch;
                (this.curShape as TextBlock).FontStyle = (sender as TextBox).FontStyle;
                (this.curShape as TextBlock).FontWeight = (sender as TextBox).FontWeight;
                (this.curShape as TextBlock).Foreground = (sender as TextBox).Foreground;
                (this.curShape as TextBlock).Opacity = (sender as TextBox).Opacity;
                Canvas.SetZIndex((this.curShape as TextBlock),Canvas.GetZIndex(sender as TextBox));
                Canvas.SetLeft(this.curShape, Canvas.GetLeft(sender as TextBox));
                Canvas.SetTop(this.curShape, Canvas.GetTop(sender as TextBox));
                this.Canvas.Children.Remove(sender as TextBox);
                if ((this.curShape as TextBlock).Text != "")
                {
                    this.Canvas.Children.Add(this.curShape);
                    ShapeWrapper shape = new ShapeWrapper(this.curShape, this.nameGenerator.Generate(this.curShape));
                    shape.EditText += EditTextBlockText;
                    this.AddShape(shape);
                    this.changed = true;
                }
                this.curShape = null;
            }
            else
            {
                string name = (this.curShape as TextBox).Name;
                name = name.Substring(0, name.Length - 7);
                this.curShape = null;
                foreach (UIElement elem in this.Canvas.Children)
                {
                    if (elem is TextBlock && (elem as TextBlock).Name == name)
                    {
                        this.curShape = elem as FrameworkElement;
                    }
                }
                (this.curShape as TextBlock).Text = (sender as TextBox).Text;
                (this.curShape as TextBlock).Width = (sender as TextBox).ActualWidth;
                (this.curShape as TextBlock).Height = (sender as TextBox).ActualHeight;
                this.Canvas.Children.Remove(sender as TextBox);
                (this.curShape as TextBlock).Visibility = Visibility.Visible;
                this.changed = true;
                this.curShape = null;
            }
        }

        public void EditTextBlockText(object sender, InputEventArgs e)
        {
            this.RemoveSelection();
            this.currShapeType = ShapeType.TextBox;
            this.curShape = new TextBox();
            (this.curShape as TextBox).Text = (sender as TextBlock).Text;
            (this.curShape as TextBox).AcceptsReturn = true;
            (this.curShape as TextBox).MinWidth = 48;
            (this.curShape as TextBox).MinHeight = 18;
            (this.curShape as TextBox).Foreground = (sender as TextBlock).Foreground;
            (this.curShape as TextBox).Opacity = (sender as TextBlock).Opacity;
            Canvas.SetZIndex((this.curShape as TextBox), Canvas.GetZIndex(sender as TextBlock));
            Canvas.SetLeft(this.curShape, Canvas.GetLeft(sender as TextBlock));
            Canvas.SetTop(this.curShape, Canvas.GetTop(sender as TextBlock));
            (this.curShape as TextBox).LostFocus += TextBoxLostFocus;
            (this.curShape as TextBox).Name = (sender as TextBlock).Name + "_txtBox";
            (sender as TextBlock).Visibility = Visibility.Hidden;
            this.Canvas.Children.Add(this.curShape);
            this.curShape.Focus();
        }

        #region canvas mouse events handlers

        private void CanvasMouseDown(object sender, MouseButtonEventArgs e)
        {
            this.showPreviewSettings();
            this.RemoveSelection();

            this.mousePos = e.GetPosition(this.Canvas);

            if (this.currShapeType == ShapeType.Rectangle || this.currShapeType == ShapeType.Ellipse)
            {
                if(this.currShapeType == ShapeType.Rectangle)
                    curShape = new Rectangle();
                else if(this.currShapeType == ShapeType.Ellipse)
                    curShape = new Ellipse();
                (curShape as Shape).Stroke = (settingsShape.Shape as Rectangle).Stroke;
                (curShape as Shape).StrokeThickness = (settingsShape.Shape as Rectangle).StrokeThickness;
                (curShape as Shape).Opacity = (settingsShape.Shape as Rectangle).Opacity;
                Canvas.SetZIndex(curShape, Canvas.GetZIndex((settingsShape.Shape as Rectangle)));
                Canvas.SetLeft(curShape, mousePos.X);
                Canvas.SetTop(curShape, mousePos.Y);
                this.Canvas.Children.Add(curShape);
            }
            else if (this.currShapeType == ShapeType.TextBlock)
            {
                this.currShapeType = ShapeType.TextBox;
                curShape = new TextBox();
                (curShape as TextBox).Text = "";
                (curShape as TextBox).AcceptsReturn = true;
                (curShape as TextBox).MinWidth = 48;
                (curShape as TextBox).MinHeight = 18;
                (curShape as TextBox).Name = "New";
                (curShape as TextBox).Foreground = (settingsShape.Shape as Rectangle).Stroke;
                (curShape as TextBox).Opacity = (settingsShape.Shape as Rectangle).Opacity;
                Canvas.SetZIndex(curShape, Canvas.GetZIndex((settingsShape.Shape as Rectangle)));
                Canvas.SetLeft(curShape, mousePos.X);
                Canvas.SetTop(curShape, mousePos.Y);
                (curShape as TextBox).LostFocus += TextBoxLostFocus;
                this.Canvas.Children.Add(curShape);
                curShape.Focus();
            }
            else if (this.currShapeType == ShapeType.TextBox && this.curShape is TextBox)
            {
                this.TextBoxLostFocus(this.curShape, null);
            }
            else if(this.currShapeType == ShapeType.Line)
            {
                curShape = new Line();
                (curShape as Line).Stroke = new SolidColorBrush(Colors.Black);
                (curShape as Line).StrokeThickness = 1;
                (curShape as Line).X1 = mousePos.X;
                (curShape as Line).Y1 = mousePos.Y;
                (curShape as Line).X2 = mousePos.X;
                (curShape as Line).Y2 = mousePos.Y;
                (curShape as Shape).Stroke = (settingsShape.Shape as Rectangle).Stroke;
                (curShape as Shape).StrokeThickness = (settingsShape.Shape as Rectangle).StrokeThickness;
                (curShape as Shape).Opacity = (settingsShape.Shape as Rectangle).Opacity;
                Canvas.SetZIndex(curShape, Canvas.GetZIndex((settingsShape.Shape as Rectangle)));
                this.Canvas.Children.Add(curShape);
            }
            else if (this.currShapeType == ShapeType.Pencil)
            {
                curShape = new Polyline();
                (curShape as Polyline).Stroke = (settingsShape.Shape as Rectangle).Stroke;
                (curShape as Polyline).StrokeThickness = (settingsShape.Shape as Rectangle).StrokeThickness;
                (curShape as Polyline).Opacity = (settingsShape.Shape as Rectangle).Opacity;
                Canvas.SetZIndex(curShape, Canvas.GetZIndex((settingsShape.Shape as Rectangle)));
                (curShape as Polyline).Points = new PointCollection();
                (curShape as Polyline).Points.Add(mousePos);
                this.Canvas.Children.Add(curShape);
            }
        }

        private void CanvasMouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton != MouseButtonState.Pressed || this.curShape == null)
            {
                return;
            }

            if (this.currShapeType == ShapeType.Rectangle || this.currShapeType == ShapeType.Ellipse)
            {
                double x = Math.Min(e.GetPosition(this.Canvas).X, mousePos.X);
                var y = Math.Min(e.GetPosition(this.Canvas).Y, mousePos.Y);

                var w = Math.Max(e.GetPosition(this.Canvas).X, mousePos.X) - x;
                var h = Math.Max(e.GetPosition(this.Canvas).Y, mousePos.Y) - y;

                curShape.Width = w;
                curShape.Height = h;

                Canvas.SetLeft(curShape, x);
                Canvas.SetTop(curShape, y);
                this.changed = true;
            }
            else if(this.currShapeType == ShapeType.Line)
            {
                (curShape as Line).X2 = e.GetPosition(this.Canvas).X;
                (curShape as Line).Y2 = e.GetPosition(this.Canvas).Y;
                this.changed = true;
            }
            else if (this.currShapeType == ShapeType.Pencil)
            {
                (curShape as Polyline).Points.Add(e.GetPosition(this.Canvas));
            }
        }

        private void CanvasMouseUp(object sender, MouseEventArgs e)
        {
            if (this.curShape == null)
            {
                return;
            }

            if (this.currShapeType == ShapeType.Rectangle || this.currShapeType == ShapeType.Ellipse)
            {
                if (this.curShape.Width < 3 && this.curShape.Height < 3)
                {
                    this.Canvas.Children.Remove(this.curShape);
                    return;
                }
                (this.curShape as Shape).Fill = new SolidColorBrush(Colors.Transparent);
                ShapeWrapper shape = new ShapeWrapper(this.curShape, this.nameGenerator.Generate(this.curShape));
                this.curShape = null;
                this.AddShape(shape);
                this.changed = true;
            }
            else if(this.currShapeType == ShapeType.Line)
            {
                (curShape as Line).X2 = e.GetPosition(this.Canvas).X;
                (curShape as Line).Y2 = e.GetPosition(this.Canvas).Y;

                double width = Math.Abs((this.curShape as Line).X1 - (this.curShape as Line).X2);
                double height = Math.Abs((this.curShape as Line).Y1 - (this.curShape as Line).Y2);
                if ( width < 3 && height < 3)
                {
                    this.Canvas.Children.Remove(this.curShape);
                    return;
                }

                ShapeWrapper shape = new ShapeWrapper(this.curShape, this.nameGenerator.Generate(this.curShape));
                this.curShape = null;
                this.AddShape(shape);
                this.changed = true;
            }
            else if (this.currShapeType == ShapeType.Pencil)
            {
                (curShape as Polyline).Points.Add(e.GetPosition(this.Canvas));
                ShapeWrapper shape = new ShapeWrapper(this.curShape, this.nameGenerator.Generate(this.curShape));
                this.curShape = null;
                this.AddShape(shape);
                this.changed = true;
            }
        }

        private void CanvasMouseLeave(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
                this.CanvasMouseUp(sender, e);
        }


        #endregion
    }
}